from typing import Optional
from fastapi import Request, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from fastapi.security.utils import get_authorization_scheme_param
# jwt相关
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials, OAuth2PasswordRequestForm
from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone
import jwt

from exception.customException import OperateException

pwd_context = CryptContext(schemes=["bcrypt"])
security = HTTPBearer()
secret_key = 'xh_blog'


def generate_token(id: str) -> str:
    expiration = datetime.now(timezone.utc) + timedelta(minutes=60*24*3)
    payload = {"id": id, "exp": expiration}
    return jwt.encode(payload, secret_key, algorithm="HS256")


def decode_token(token: str) -> dict:
    return jwt.decode(token, secret_key, algorithms="HS256")


class MyOAuth2PasswordBearer(OAuth2PasswordBearer):
    '''
    docs文档的接口，如果需要登录后才能访问，
    需要添加OAuth2PasswordBearer的依赖才会展示登录入口，
    并且依赖了OAuth2PasswordBearer的接口才会带有登录信息
    全局添加OAuth2PasswordBearer依赖，则登录接口会陷入死循环，因为登录接口没有OAuth2PasswordBearer的信息
    重写OAuth2PasswordBearer，对于登录接口，或者指定的接口不读取OAuth2PasswordBearer，直接返回空字符串
    '''

    def __init__(self, tokenUrl: str):
        super().__init__(
            tokenUrl=tokenUrl,
            scheme_name=None,
            scopes=None,
            description=None,
            auto_error=True
        )

    async def __call__(self, request: Request) -> Optional[dict]:
        payload: dict = {}
        path: str = request.get('path')
        if path.startswith('/sysUser/token') | path.startswith('/sysUser/login') | path.startswith(
                '/docs') | path.startswith('/openapi'):
            return None
        authorization: str = request.headers.get("Authorization")

        scheme, token = get_authorization_scheme_param(authorization)
        if not authorization or scheme.lower() != "bearer":
            if self.auto_error:
                raise OperateException(10000, "没有token")  # 就这么简单
            else:
                return None
        else:
            # param = param + "123"
            try:
                payload = decode_token(token)
            except jwt.exceptions.ExpiredSignatureError:
                raise OperateException(10001, "token失效，请重新登录")  # 就这么简单
            except jwt.InvalidSignatureError:
                raise OperateException(10002, "token非法")  # 就这么简单
        return payload


# 添加jwt验证
my_oauth2_scheme = MyOAuth2PasswordBearer(tokenUrl='/sysUser/token')
